﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Abp.AspNetCore.Mvc.Controllers;
using Abp.Authorization;
using Abp.MultiTenancy;
using Abp.Runtime.Caching;
using Abp.Web.Models;
using AVISOutBound.SSOServer.Web.Models.Account;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Authorization;
using AVISOutBound.SSOServer.Web.Models.AccountViewModels;
using Microsoft.AspNetCore.Identity;
using AVISOutBound.SSOServer.Web.Models;
using AVISOutBound.SSOServer.VerifyCode;
using AVISOutBound.SSOServer.Dto;
using AVISOutBound.SSOServer.Authorization.Members;
using AVISOutBound.SSOServer.Identity;
using Microsoft.AspNetCore.Authentication;
using Abp.UI;

namespace AVISOutBound.SSOServer.Web.Controllers
{
    /// <summary>
    /// 用户注册登录控制器
    /// </summary>
    [Authorize]
    public class AccountController : SSOServerControllerBase
    {
        #region 构造函数
        private readonly ABGUserManager _userManager;
        private readonly ABGSignInManager _signInManager;
        private readonly ABGUserRegistrationManager _userRegistrationManager;
        private readonly ICacheManager _cacheManager;
        IValidateCodeAppService validateCodeAppService;

        public AccountController(
            ABGUserManager userManager,
            ABGSignInManager signInManager,
            ABGUserRegistrationManager userRegistrationManager,
            IValidateCodeAppService validateCodeAppService,
            ICacheManager cacheManager)
        {
            this._userManager = userManager;
            this._signInManager = signInManager;
            this._userRegistrationManager = userRegistrationManager;
            this._cacheManager = cacheManager;
            this.validateCodeAppService = validateCodeAppService;
        }
        #endregion

        [HttpGet]
        [AllowAnonymous]
        public async Task<IActionResult> Login(string returnUrl = null)
        {
            // Clear the existing external cookie to ensure a clean login process
            await HttpContext.SignOutAsync(IdentityConstants.ExternalScheme);

            ViewData["ReturnUrl"] = returnUrl;
            return View();
        }

        [HttpPost]
        [AllowAnonymous]
        [ValidateAntiForgeryToken]
        public async Task<JsonResult> Login(LoginInfo loginModel, string returnUrl = "", string returnUrlHash = "")
        {
            var _loginInfo = _cacheManager.GetCache(SSOServerConsts.CacheLoginInfo).GetOrDefault(loginModel.Phone) as LoginInfo;
            if (_loginInfo == null)
            {
                _loginInfo = loginModel;
            }
            if (_loginInfo.ErrorCount > 3)
                if (string.IsNullOrEmpty(loginModel.ValidateCode))
                    return Json(new AjaxResponse(new { Success = false, Msg = "输入密码错误超过3次，请输入验证码！" }));
                else if (validateCodeAppService.CheckValidateCode(_loginInfo.Phone, _loginInfo.ValidateCode) == false)
                    return Json(new AjaxResponse(new { Success = false, Msg = "验证码错误！" }));

            returnUrl = NormalizeReturnUrl(returnUrl);
            if (!string.IsNullOrWhiteSpace(returnUrlHash))
            {
                returnUrl = returnUrl + returnUrlHash;
            }

            var user = await _userManager.FindByEmailAsync(loginModel.Phone);
            if (user == null)
                return Json(new AjaxResponse(new { Success = false, Msg = "账户不存在，请先注册！", LoginInfo = _loginInfo }));
            var result = await _signInManager.PasswordSignInAsync(user, loginModel.Password, loginModel.RememberMe, lockoutOnFailure: false);


            if (result.Succeeded == true)
            {
                _loginInfo.ErrorCount = 0;
                _loginInfo.ValidateCode = "";
                _cacheManager.GetCache(SSOServerConsts.CacheLoginInfo).Set(_loginInfo.Phone, _loginInfo);
                await UnitOfWorkManager.Current.SaveChangesAsync();
                return Json(new AjaxResponse(new { Success = true, TargetUrl = returnUrl, LoginInfo = _loginInfo }));
            }
            else
            {
                _loginInfo.ErrorCount += 1;
                _cacheManager.GetCache(SSOServerConsts.CacheLoginInfo).Set(_loginInfo.Phone, _loginInfo);
                return Json(new AjaxResponse(new { Success = false, Msg = "手机号或密码错误！", LoginInfo = _loginInfo }));
            }
        }

        [HttpPost]
        [ValidateAntiForgeryToken]
        public async Task<IActionResult> Logout()
        {
            await _signInManager.SignOutAsync();
            Logger.Info("User logged out.");
            return RedirectToAction(nameof(HomeController.Index), "Home");
        }

        private IActionResult RedirectToLocal(string returnUrl)
        {
            if (Url.IsLocalUrl(returnUrl))
            {
                return Redirect(returnUrl);
            }
            else
            {
                return RedirectToAction(nameof(HomeController.Index), "Home");
            }
        }


        [HttpPost]
        [AllowAnonymous]
        public ValidateCodeDto CreateValidate()
        {
            ValidateCodeDto vcode = validateCodeAppService.CreateValidateCode();
            return vcode;
        }

        [HttpPost]
        [AllowAnonymous]
        public virtual JsonResult SendSmsCode(SendSmsCodeModel sendSmsCodeModel)
        {
            if (validateCodeAppService.CheckValidateCode(sendSmsCodeModel.Phone, sendSmsCodeModel.ValidateCode) == false)
                return Json(new AjaxResponse(new { Success = false, Msg = "验证码错误！" }));
            if (!string.IsNullOrEmpty(validateCodeAppService.GetSmsCode(sendSmsCodeModel.Phone)))
                return Json(new AjaxResponse(new { Success = false, Msg = "手机短信已发送，耐心等待！" }));
            validateCodeAppService.SendSmsCode(sendSmsCodeModel.Phone);
            return Json(new AjaxResponse());
        }
        [HttpPost]
        [AllowAnonymous]
        public async Task<JsonResult> LoginOrRegisterByPhone(LoginOrRegisterViewModel model)
        {
            if (validateCodeAppService.CheckSmsCode(model.Phone, model.SmsCode) == false)
                return Json(new AjaxResponse(new { Success = false, Msg = "短信验证码错误！" }));
            var user = await _userManager.FindByEmailAsync(model.Phone);
            if (user == null)
            {
                //TODO:注册用户
            }
            await _signInManager.SignInAsync(user, false);
            return Json(new AjaxResponse { Success = true, TargetUrl = GetAppHomeUrl() });
        }

        #region 短信快速登录

        /*
         * 请求地址：/Account/SendCodeByPhone
         * 输入手机号码，该手机号码需要绑定到具体的一个用户，可以用密码登录之后，点击账号之后进行修改
         * 输入短信验证码之后就可以登录了。
         */

        //
        // GET: /Account/SendCodeByPhone
        [HttpGet]
        [AllowAnonymous]
        public async Task<ActionResult> SendCodeByPhone(string phone)
        {
            // 暂时写死手机号码
            phone = phone ?? "18367300126";
            if (string.IsNullOrWhiteSpace(phone))
            {
                // TODO 提醒用户，手机号码为必填，不能为空。
                return View("Error");
            }

            // TODO 需要封装userManager，增加方法FindByPhoneNumberAsync
            var user = _userManager.Users.FirstOrDefault(u => u.PhoneNumber == phone);
            if (user == null)
            {
                // TODO 如果用户不存在，则考虑直接注册一个用户
                var member = await _userRegistrationManager.RegisterAsync(phone);

                return View("Error");
            }

            // Generate the token and send it
            var code = await _userManager.GenerateTwoFactorTokenAsync(user, "Phone");
            if (string.IsNullOrWhiteSpace(code))
            {
                return View("Error");
            }

            var message = "Your security code is: " + code;
            //await _smsSender.SendSmsAsync(await _userManager.GetPhoneNumberAsync(user), message);

            return RedirectToAction(nameof(VerifyCodeByPhone), new { Phone = phone });
        }

        //
        // GET: /Account/VerifyCodeByPhone
        [HttpGet]
        [AllowAnonymous]
        public IActionResult VerifyCodeByPhone(string phone)
        {
            // Require that the user has already logged in via username/password or external login
            //var user = await _signInManager.GetTwoFactorAuthenticationUserAsync();
            //if (user == null)
            //{
            //    return View("Error");
            //}

            ViewBag.Phone = phone;
            return View();
        }

        //
        // POST: /Account/VerifyCode
        [HttpPost]
        [AllowAnonymous]
        [ValidateAntiForgeryToken]
        public async Task<IActionResult> VerifyCodeByPhone(LoginOrRegisterViewModel model)
        {
            // The following code protects for brute force attacks against the two factor codes.
            // If a user enters incorrect codes for a specified amount of time then the user account
            // will be locked out for a specified amount of time.
            var result = await _signInManager.PhoneNumberSignInAsync(model.Phone, model.SmsCode, false, false);
            if (result.Succeeded)
            {
                return RedirectToLocal("");
            }
            if (result.IsLockedOut)
            {
                Logger.Warn("User account locked out.");
                return View("Lockout");
            }
            else
            {
                ModelState.AddModelError(string.Empty, "Invalid code.");
                return View(model);
            }
        }

        //
        // GET: /Account/RegisterByPhone
        // 测试多语言的Bug，并完善多语言方案
        [HttpGet]
        [AllowAnonymous]
        public async Task<IActionResult> RegisterByPhone(string mobile)
        {
            // 暂时写死手机号码
            if (string.IsNullOrWhiteSpace(mobile))
            {
                throw new UserFriendlyException("电话号码不能为空，请在地址栏中带上mobile参数");
            }

            // TODO 这里会报多语言的错误
            var member = await _userRegistrationManager.RegisterAsync(mobile);

            return Content("多语言的Bug已经修复！");
        }

        #endregion

        #region 私有方法


        private string GetAppHomeUrl()
        {
            return Url.Action("Index", "Home");
        }

        private string NormalizeReturnUrl(string returnUrl, Func<string> defaultValueBuilder = null)
        {
            if (defaultValueBuilder == null)
            {
                defaultValueBuilder = GetAppHomeUrl;
            }

            if (string.IsNullOrEmpty(returnUrl))
            {
                return defaultValueBuilder();
            }

            if (Url.IsLocalUrl(returnUrl))
            {
                return returnUrl;
            }

            return defaultValueBuilder();
        }
        #endregion
    }
}